/*
 * Copyright (C) 2017 skydoves
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.skydoves.powermenu;

import com.skydoves.powermenu.annotations.Dp;
import com.skydoves.powermenu.annotations.Sp;
import com.skydoves.powermenu.shape.ShapeUtil;

import ohos.aafwk.ability.ILifecycle;
import ohos.aafwk.ability.Lifecycle;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.DependentLayout;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.LayoutScatter;
import ohos.agp.components.ListContainer;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.text.Font;
import ohos.agp.utils.LayoutAlignment;
import ohos.agp.utils.TextAlignment;
import ohos.agp.window.dialog.CommonDialog;
import ohos.agp.window.dialog.PopupDialog;
import ohos.app.Context;

import java.util.ArrayList;
import java.util.List;

/**
 * PowerMenu is one the implementation of the {@link AbstractPowerMenu}.
 *
 * <p>It implements the popup showing with the item selected effect by {@link MenuListAdapter}.
 *
 * <p>{@link PowerMenuItem} is the member of the PowerMenu's list.
 */
public class TransPowerMenu extends AbstractPowerMenu<PowerMenuItem, MenuListAdapter> implements IPowerMenuAdapter {
    private DependentLayout binding;

    protected TransPowerMenu(Context context, AbstractMenuBuilder abstractMenuBuilder, Component component) {
        super(context, abstractMenuBuilder, component);
        Builder builder = (Builder) abstractMenuBuilder;
        setSelectedEffect(builder.selectedEffect);
        if (builder.menuItemClickListener != null) {
            setOnMenuItemClickListener(builder.menuItemClickListener);
        }
        if (builder.textColor != -2) {
            setTextColor(builder.textColor);
        }
        if (builder.menuColor != -2) {
            setMenuColor(builder.menuColor);
        }
        if (builder.selectedTextColor != -2) {
            setSelectedTextColor(builder.selectedTextColor);
        }
        if (builder.selectedMenuColor != -2) {
            setSelectedMenuColor(builder.selectedMenuColor);
        }
        if (builder.selected != -1) {
            setSelectedPosition(builder.selected);
        }
        if (builder.textSize != 12) {
            setTextSize(builder.textSize);
        }
        if (builder.textGravity != TextAlignment.START) {
            setTextGravity(builder.textGravity);
        }
        if (builder.textFont != null) {
            setTextFont(builder.textFont);
        }
        if (builder.iconSize != 35) {
            setIconSize(builder.iconSize);
        }
        if (builder.iconPadding != 7) {
            setIconPadding(builder.iconPadding);
        }
        if (builder.iconColor != -2) {
            setIconColor(builder.iconColor);
        }
        if (builder.headerView != null) {
            builder.headerView.setBackground(ShapeUtil.createRectangleCornersDrawable((ShapeElement) builder.headerView.getBackgroundElement(), radius, radius, 0, 0));
            menuHeaderView.addComponent(builder.headerView);
        }
        if (builder.footerView != null) {
            menuFooterView.addComponent(builder.footerView, new ComponentContainer.LayoutConfig(ComponentContainer.LayoutConfig.MATCH_PARENT, ComponentContainer.LayoutConfig.MATCH_PARENT));
        } else {
            DirectionalLayout listParent = (DirectionalLayout) menuListView.getComponentParent();
            listParent.setBackground(ShapeUtil.createRectangleCornersDrawable((ShapeElement) listParent.getBackgroundElement(), 0, 0, radius, radius));
            menuListView.setBackground(ShapeUtil.createRectangleCornersDrawable((ShapeElement) menuListView.getBackgroundElement(), 0, 0, radius, radius));
        }
        getAdapter().setItemClickListener(itemClickListener);
        setRadius(builder.menuRadius);
        addItemList(builder.powerMenuItems);
        this.menuListView.setItemProvider(adapter);
        if (menuWindow instanceof PopupDialog) {
            ((PopupDialog) menuWindow).setCustomComponent(backgroundView);
        }
    }

    @Override
    protected void initialize(Context context, Boolean isMaterial, Component component) {
        LayoutScatter layoutInflater = LayoutScatter.getInstance(context);
        binding = (DependentLayout) layoutInflater.parse(ResourceTable.Layout_layout_power_menu_library_trans, null, false);
        super.initialize(context, isMaterial, component);
        adapter = new MenuListAdapter(menuListView);
    }

    @Override
    Component getMenuRoot(Boolean isMaterial) {
        return binding.findComponentById(ResourceTable.Id_power_menu_layout);
    }

    DependentLayout getMenuBackground(Boolean isMaterial) {
        return binding;
    }

    DirectionalLayout getMenuHeaderView(Boolean isMaterial) {
        return (DirectionalLayout) binding.findComponentById(ResourceTable.Id_head);
    }

    DirectionalLayout getMenuFooterView(Boolean isMaterial) {
        return (DirectionalLayout) binding.findComponentById(ResourceTable.Id_foot);
    }

    @Override
    ListContainer getMenuList(Boolean isMaterial) {
        return (ListContainer) binding.findComponentById(ResourceTable.Id_power_menu_listView);
    }

    @Override
    public void setTextColor(int color) {
        this.getAdapter().setTextColor(color);
    }

    @Override
    public void setMenuColor(int color) {
        this.getAdapter().setMenuColor(color);
    }

    @Override
    public void setSelectedTextColor(int color) {
        this.getAdapter().setSelectedTextColor(color);
    }

    @Override
    public void setSelectedMenuColor(int color) {
        this.getAdapter().setSelectedMenuColor(color);
    }

    @Override
    public void setSelectedEffect(boolean selectedEffect) {
        getAdapter().setSelectedEffect(selectedEffect);
    }

    @Override
    public void setIconSize(int iconSize) {
        getAdapter().setIconSize(iconSize);
    }

    @Override
    public void setIconColor(int iconColor) {
        getAdapter().setIconColor(iconColor);
    }

    @Override
    public void setIconPadding(int iconPadding) {
        getAdapter().setIconPadding(iconPadding);
    }

    @Override
    public void setRadius(float radius) {
        getAdapter().setRadius(radius);
    }

    @Override
    public void setTextSize(@Sp int size) {
        this.getAdapter().setTextSize(size);
    }

    @Override
    public void setTextGravity(int gravity) {
        this.getAdapter().setTextGravity(gravity);
    }

    @Override
    public void setTextFont(Font Font) {
        this.getAdapter().setTextFont(Font);
    }

    @Override
    public void onStateChanged(Lifecycle.Event event, Intent intent) {
    }

    @Override
    public void setItemClickListener(ListContainer.ItemClickedListener itemClickListener) {
    }

    /**
     * Builder class for creating {@link PowerMenu}.
     */
    public static class Builder extends AbstractMenuBuilder {
        private OnMenuItemClickListener<PowerMenuItem> menuItemClickListener = null;
        private int textColor = -2;
        private int menuColor = -2;
        private boolean selectedEffect = true;
        private int selectedTextColor = -2;
        private int selectedMenuColor = -2;
        private int textSize = 12;
        private int textGravity = TextAlignment.START;
        private Font textFont = null;
        private Component contentComponent;
        private int alignment = LayoutAlignment.UNSET;
        private Component anchor;

        private final List<PowerMenuItem> powerMenuItems;

        /**
         * Builder class for creating {@link PowerMenu}.
         *
         * @param context context.
         */
        public Builder(Context context) {
            this.context = context;
            this.powerMenuItems = new ArrayList<>();
            this.layoutInflater = LayoutScatter.getInstance(context);
        }

        /**
         * sets the {@link ILifecycle} for dismissing automatically when the {@link ILifecycle}
         * is destroyed. It will prevents memory leak.
         *
         * @param iLifecycle lifecycle owner
         * @return {@link Builder}.
         * @see <a href="https://github.com/skydoves/PowerMenu#avoid-memory-leak">GitHub :
         * PowerMenu-Avoid-Memory-Leak</a>
         */
        public Builder setLifecycle(ILifecycle iLifecycle) {
            this.iLifecycle = iLifecycle;
            return this;
        }

        /**
         * sets the visibility of the background popup.
         *
         * @param show visibility of the background popup.
         * @return {@link Builder}.
         */
        public Builder setShowBackground(boolean show) {
            this.showBackground = show;
            return this;
        }

        public Builder setContentComponent(Component content) {
            this.contentComponent = content;
            return this;
        }

        /**
         * sets the {@link OnMenuItemClickListener} to the popup.
         *
         * @param menuItemClickListener {@link OnMenuItemClickListener} interface.
         * @return {@link Builder}.
         */
        public Builder setOnMenuItemClickListener(
                OnMenuItemClickListener<PowerMenuItem> menuItemClickListener) {
            this.menuItemClickListener = menuItemClickListener;
            return this;
        }

        /**
         * sets the {@link Component.ClickedListener} to the popup.
         *
         * @param onBackgroundClickListener {@link Component.ClickedListener} interface.
         * @return {@link Builder}.
         */
        public Builder setOnBackgroundClickListener(Component.ClickedListener onBackgroundClickListener) {
            this.backgroundClickListener = onBackgroundClickListener;
            return this;
        }

        /**
         * sets the {@link OnDismissedListener} to the popup.
         *
         * @param onDismissListener {@link OnDismissedListener} interface.
         * @return {@link Builder}.
         */
        public Builder setOnDismissListener(OnDismissedListener onDismissListener) {
            this.onDismissedListener = onDismissListener;
            return this;
        }

        /**
         * sets the header view by layout resource.
         *
         * @param headerView layout resource
         * @return {@link Builder}.
         */
        public Builder setHeaderView(int headerView) {
            this.headerView = layoutInflater.parse(headerView, null, false);
            return this;
        }

        /**
         * sets the header view by layout view.
         *
         * @param headerView header view.
         * @return {@link Builder}.
         */
        public Builder setHeaderView(Component headerView) {
            this.headerView = headerView;
            return this;
        }

        /**
         * sets the footer view by layout resource.
         *
         * @param footerView footer view.
         * @return {@link Builder}.
         */
        public Builder setFooterView(int footerView) {
            this.footerView = layoutInflater.parse(footerView, null, false);
            return this;
        }

        /**
         * sets the footer view by layout view.
         *
         * @param footerView footer view.
         * @return {@link Builder}.
         */
        public Builder setFooterView(Component footerView) {
            this.footerView = footerView;
            return this;
        }

        /**
         * sets the menu animation to the popup.
         *
         * @param menuAnimation animation.
         * @return {@link Builder}.
         */
        public Builder setAnimation(MenuAnimation menuAnimation) {
            this.menuAnimation = menuAnimation;
            return this;
        }

        /**
         * sets the customized menu animation using resource.
         *
         * @param style animation resource.
         * @return {@link Builder}.
         */
        public Builder setAnimationStyle(int style) {
            this.animationStyle = style;
            return this;
        }

        /**
         * sets the corner radius of the popup menu.
         *
         * @param radius corner radius.
         * @return {@link Builder}.
         */
        public Builder setMenuRadius(float radius) {
            this.menuRadius = radius;
            return this;
        }

        /**
         * sets the shadow of the popup menu.
         *
         * @param shadow popup shadow.
         * @return {@link Builder}.
         */
        public Builder setMenuShadow(float shadow) {
            this.menuShadow = shadow;
            return this;
        }

        /**
         * sets the width size of the popup menu.
         *
         * @param width width size.
         * @return {@link Builder}.
         */
        public Builder setWidth(int width) {
            this.width = width;
            return this;
        }

        /**
         * sets the height size of the popup menu.
         *
         * @param height height size.
         * @return {@link Builder}.
         */
        public Builder setHeight(int height) {
            this.height = height;
            return this;
        }

        /**
         * sets the width and height size of the popup menu.
         *
         * @param width  width size.
         * @param height height size.
         * @return {@link Builder}.
         */
        public Builder setSize(int width, int height) {
            this.width = width;
            this.height = height;
            return this;
        }

        /**
         * sets a padding size of the popup menu.
         *
         * @param padding padding size.
         * @return {@link Builder}
         */
        public Builder setPadding(int padding) {
            this.padding = padding;
            return this;
        }

        /**
         * sets the content text color of the popup menu item.
         *
         * @param color menu item's content text color.
         * @return {@link Builder}.
         */
        public Builder setTextColor(int color) {
            this.textColor = color;
            return this;
        }

        /**
         * sets the content text color of the popup menu item.
         *
         * @param color menu item's content text color by resource.
         * @return {@link Builder}.
         */
        public Builder setTextColorResource(int color) {
            this.textColor = context.getColor(color);
            return this;
        }

        /**
         * sets the content text size of the popup menu item.
         *
         * @param size menu item's content text size.
         * @return {@link Builder}.
         */
        public Builder setTextSize(int size) {
            this.textSize = size;
            return this;
        }

        /**
         * sets the content text {@link TextAlignment} of the popup menu item.
         *
         * @param gravity menu item's content text gravity.
         * @return {@link Builder}.
         */
        public Builder setTextGravity(int gravity) {
            this.textGravity = gravity;
            return this;
        }

        /**
         * sets the content text {@link Font} of the popup menu item.
         *
         * @param Font menu item's content text Font.
         * @return {@link Builder}.
         */
        public Builder setTextFont(Font Font) {
            this.textFont = Font;
            return this;
        }

        /**
         * sets an icon color of the menu item.
         *
         * @param iconColor icon color of the menu item.
         * @return Builder
         */
        public Builder setIconColor(int iconColor) {
            this.iconColor = iconColor;
            return this;
        }

        /**
         * sets an icon size of the menu item.
         *
         * @param iconSize icon size of the menu item.
         * @return Builder
         */
        public Builder setIconSize(@Dp int iconSize) {
            this.iconSize = iconSize;
            return this;
        }

        /**
         * sets a padding value between icon and menu item.
         *
         * @param iconPadding padding value between icon and menu item.
         * @return Builder
         */
        public Builder setIconPadding(@Dp int iconPadding) {
            this.iconPadding = iconPadding;
            return this;
        }

        /**
         * sets the normal menu item background color.
         *
         * @param color normal menu item background color.
         * @return {@link Builder}.
         */
        public Builder setMenuColor(int color) {
            this.menuColor = color;
            return this;
        }

        /**
         * sets the normal menu item background color.
         *
         * @param color normal menu item background color by resource.
         * @return {@link Builder}.
         */
        public Builder setMenuColorResource(int color) {
            this.menuColor = context.getColor(color);
            return this;
        }

        /**
         * sets the selected menu item text color.
         *
         * @param color selected menu item text color.
         * @return {@link Builder}.
         */
        public Builder setSelectedTextColor(int color) {
            this.selectedTextColor = color;
            return this;
        }

        /**
         * sets the selected menu item text color.
         *
         * @param color selected menu item text color by resource.
         * @return {@link Builder}.
         */
        public Builder setSelectedTextColorResource(int color) {
            this.selectedTextColor = context.getColor(color);
            return this;
        }

        /**
         * sets the selected menu item background color.
         *
         * @param color selected menu item background color.
         * @return {@link Builder}.
         */
        public Builder setSelectedMenuColor(int color) {
            this.selectedMenuColor = color;
            return this;
        }

        /**
         * sets the selected menu item background color.
         *
         * @param color selected menu item background color by resource.
         * @return {@link Builder}.
         */
        public Builder setSelectedMenuColorResource(int color) {
            this.selectedMenuColor = context.getColor(color);
            return this;
        }

        /**
         * sets true or false of the menu item selected effect
         *
         * @param effect the menu item selected effect.
         * @return {@link Builder}.
         */
        public Builder setSelectedEffect(boolean effect) {
            this.selectedEffect = effect;
            return this;
        }

        /**
         * sets the divider height between the menu items.
         *
         * @param height divider height between the menu items.
         * @return {@link Builder}.
         */
        public Builder setDividerHeight(int height) {
            this.dividerHeight = height;
            return this;
        }

        /**
         * sets the color of the background popup.
         *
         * @param color color of the background popup.
         * @return {@link Builder}.
         */
        public Builder setBackgroundColor(int color) {
            this.backgroundColor = color;
            return this;
        }

        /**
         * sets the color of the background popup.
         *
         * @param color color of the background popup by resource.
         * @return {@link Builder}.
         */
        public Builder setBackgroundColorResource(int color) {
            this.backgroundColor = context.getColor(color);
            return this;
        }

        /**
         * sets the aligment of the background popup.
         *
         * @param aligment aligment of the background popup.
         * @return {@link Builder}.
         */
        public Builder setMenuAligment(int aligment) {
            this.alignment = aligment;
            return this;
        }

        /**
         * sets the focusability of the popup menu.
         *
         * @param focusable focusability of the popup menu.
         * @return {@link Builder}.
         */
        public Builder setFocusable(boolean focusable) {
            this.focusable = focusable;
            return this;
        }

        /**
         * sets the initialized selected effect menu item position.
         *
         * @param position initialized selected effect menu item position.
         * @return {@link Builder}.
         */
        public Builder setSelected(int position) {
            this.selected = position;
            return this;
        }

        /**
         * sets the clipping or not of the popup menu.
         *
         * @param isClipping clipping or not of the popup menu.
         * @return {@link Builder}.
         */
        public Builder setIsClipping(boolean isClipping) {
            this.isClipping = isClipping;
            return this;
        }

        /**
         * sets the dismissing automatically when the menu item is clicked.
         *
         * @param autoDismiss dismissing automatically when the menu item is clicked.
         * @return {@link Builder}.
         */
        public Builder setAutoDismiss(boolean autoDismiss) {
            this.autoDismiss = autoDismiss;
            return this;
        }

        /**
         * sets the dismiss action if already popup is showing.
         *
         * <p>Recommend to use with setFocusable(true) and setShowBackground(false).
         *
         * @param dismissIfShowAgain dismiss if already popup is showing.
         * @return Builder
         */
        public Builder setDismissIfShowAgain(boolean dismissIfShowAgain) {
            this.dismissIfShowAgain = dismissIfShowAgain;
            return this;
        }

        /**
         * adds an {@link PowerMenuItem} item to the popup menu list.
         *
         * @param item {@link PowerMenuItem} item.
         * @return {@link Builder}.
         */
        public Builder addItem(PowerMenuItem item) {
            this.powerMenuItems.add(item);
            return this;
        }

        /**
         * adds an {@link PowerMenuItem} item to the popup menu list at position.
         *
         * @param position specific position.
         * @param item     {@link PowerMenuItem} item.
         * @return {@link Builder}.
         */
        public Builder addItem(int position, PowerMenuItem item) {
            this.powerMenuItems.add(position, item);
            return this;
        }

        /**
         * adds a list of the {@link PowerMenuItem} item to the popup menu list.
         *
         * @param itemList list of the {@link PowerMenuItem}.
         * @return {@link Builder}.
         */
        public Builder addItemList(List<PowerMenuItem> itemList) {
            this.powerMenuItems.addAll(itemList);
            return this;
        }

        /**
         * sets the preference name of the popup menu. For persistence the clicked item position. If the
         * preference name is set, the persisted position item will be clicked automatically when the
         * popup menu initialized. This option should be used with setInitializeRule and
         * setILifecycle.
         *
         * @param preferenceName name of the popup menu.
         * @return {@link Builder}.
         * @see <a href="https://github.com/skydoves/PowerMenu#preference"</a>
         */
        public Builder setPreferenceName(String preferenceName) {
            this.preferenceName = preferenceName;
            return this;
        }

        /**
         * sets the initialization rule of the recovering persisted position.
         *
         * @param event           when should be recovered.
         * @param defaultPosition default selected position.
         * @return {@link Builder}.
         */
        public Builder setInitializeRule(Lifecycle.Event event, int defaultPosition) {
            this.initializeRule = event;
            this.defaultPosition = defaultPosition;
            return this;
        }

        /**
         * sets the circular revealed effect using {@link CircularEffect}.
         *
         * @param circularEffect circular revealed effect using {@link CircularEffect}.
         * @return @return {@link Builder}.
         */
        public Builder setCircularEffect(CircularEffect circularEffect) {
            this.circularEffect = circularEffect;
            return this;
        }

        /**
         * sets the menu layout should be composed of material components.
         *
         * @param isMaterial is material layout or not.
         * @return @return {@link Builder}.
         */
        public Builder setIsMaterial(Boolean isMaterial) {
            this.isMaterial = isMaterial;
            return this;
        }

        public Builder setAnchor(Component anchor) {
            this.anchor = anchor;
            return this;
        }

        public TransPowerMenu build() {
            return new TransPowerMenu(context, this, anchor);
        }
    }

    /**
     * An abstract factory class for creating an instance of {@link PowerMenu}.
     *
     * <p>A factory implementation class must have a non-argument constructor.
     */
    public abstract static class Factory {
        /**
         * TransPowerMenu instance
         *
         * @param context   context
         * @param lifecycle lifecycle
         * @return PowerMenu
         */
        public abstract TransPowerMenu create(
                Context context, ILifecycle lifecycle);
    }
}
